#ifndef DESCENT_DETAIL_ENTITY_HPP_INCLUDED
#define DESCENT_DETAIL_ENTITY_HPP_INCLUDED

/**
 * @file 
 */

#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <boost/any.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include "descent/log.hpp"
#include "descent/pointers.hpp"

namespace fs = boost::filesystem;
namespace py = boost::python;

namespace descent {
typedef std::vector<component_ptr_t> comp_con_t;
/**
 * Predicate for finding components by name.
 */
class comp_name_equal : public std::unary_function<const_component_ptr_t, bool> {
public:
	/**
	 * @param name 
	 */
	comp_name_equal(const std::string& name): name_(name) {}
	virtual bool operator()(const_component_ptr_t c) const {
		return name_ == c->name();
	}
private:
	const std::string name_;
};
/**
 * Predicate for finding components by ID.
 */
class comp_id_equal : public std::unary_function<const_component_ptr_t, bool> {
public:
	/**
	 * @param id 
	 */
	comp_id_equal(unsigned long id): id_(id) {}
	virtual bool operator()(const_component_ptr_t c) const {
		return id_ == c->id();
	}
private:
	const unsigned long id_;
};
/**
 * Entity.
 */
class entity : public boost::enable_shared_from_this<entity> {
public:
	entity();
	~entity();
	/**
	 * Imports an entity from an XML file.
	 * When called multiple times, any XML is simply reparsed, adding any components regardless of whether or not they already exist.
	 * When an entity has a parent, this function is called recursively, loading all parent entities.
	 * @param file Path to the XML file.
	 */
	void import_xml(const fs::path& file);
	/**
	 * Extracts any and all matching attributes.
	 * Since multiple components of the same name (class) may be attached, multiple attributes may be found.
	 * It is possible that different components have attributes of the same name, but this is highly discouraged and should not occur.
	 * @param T Native type of the attribute.
	 * @param name Name of the attribute.
	 * @param attribs Container of pairs of pointers to attributes and their corresponding components. The container is populated with any matching attributes.
	 */
	template<typename T>
	void extract(const typename attrib_con_t::first_type& name, std::vector<std::pair<T*, component_ptr_t> >& attribs) {
		BOOST_FOREACH(comp_con_t::value_type c, comps_) {
			T* value;
			if ((value = c->extract<T>(name))) {
				attribs.push_back(std::pair<T*, component_ptr_t>(value, c));
			}
		}
	}
	/**
	 * Extracts any and all matching attributes.
	 * Since multiple components of the same name (class) may be attached, multiple attributes may be found.
	 * It is possible that different components have attributes of the same name, but this is highly discouraged and should not occur.
	 * @param T Native type of the attribute.
	 * @param name Name of the attribute.
	 * @param attribs Container of pairs of pointers to attributes and their corresponding components. The container is populated with any matching attributes.
	 */
	template<typename T>
	void extract(const typename attrib_con_t::first_type& name, std::vector<std::pair<const T*, const_component_ptr_t> >& attribs) const {
	}
	/**
	 * Executes any and all matching actions.
	 * @param name 
	 * @param results 
	 */
	void execute(const typename action_con_t::first_type& name, std::vector<std::pair<py::object, component_ptr_t> >& results);
	/**
	 * Executes the first (and only the first) matching action.
	 * @param name 
	 * @return 
	 */
	py::object execute(const typename action_con_t::first_type& name);
	/**
	 * @return 
	 */
	const std::string& name() const;
	/**
	 * @return 
	 */
	unsigned long id() const;
private:
	/**
	 * Adds a component.
	 * @param c Component to add.
	 * @return True if the component was added, otherwise false.
	 */
	bool augment(component_ptr_t c);
	/**
	 * Removes a component.
	 * @param c Component to remove.
	 * @return True if a component was removed, otherwise false.
	 */
	bool reduce(component_ptr_t c);
	/// ID seed/source.
	static unsigned long next_id_;
	/// Name (class).
	std::string name_;
	/// ID.
	unsigned long id_;
	/// Container of components.
	comp_con_t comps_;
};
/**
 * Extracts any and all matching attributes from an entity.
 * @param T 
 * @param e 
 * @param name 
 * @param attribs 
 */
template<attrib_data_type T>
void extract(entity_ptr_t e, const typename attrib_con_t::first_type& name, std::vector<std::pair<typename attrib_traits<T>::native_type*, component_ptr_t> >& attribs) {
	e->extract<typename attrib_traits<T>::native_type>(name, attribs);
}
template<attrib_data_type T>
void extract(const_entity_ptr_t e, const typename attrib_con_t::first_type& name, std::vector<std::pair<const typename attrib_traits<T>::native_type*, const_component_ptr_t> >& attribs) {
	e->extract<typename attrib_traits<T>::native_type>(name, attribs);
}
/**
 * Extracts the first (and only the first) matching attribute from an entity.
 * @param T 
 * @param name 
 * @return Pair containing a pointer to the attribute and a pointer to its corresponding component. Both pointers are null if the attribute is not found.
 */
template<attrib_data_type T>
std::pair<typename attrib_traits<T>::native_type*, component_ptr_t> extract(entity_ptr_t e, const typename attrib_con_t::first_type& name) {
	std::vector<std::pair<typename attrib_traits<T>::native_type*, component_ptr_t> > attribs;
	extract<T>(e, attribs);
	if (!attribs.empty()) {
		return attribs[0];
	}
	else {
		//log().warning() << "\n";
	}
	return std::pair<typename attrib_traits<T>::native_type*, component_ptr_t>(NULL, component_ptr_t());
}
/**
 * Extracts the first (and only the first) matching attribute from an entity.
 * @param T 
 * @param name 
 * @return Pair containing a pointer to the attribute and a pointer to its corresponding component. Both pointers are null if the attribute is not found.
 */
template<attrib_data_type T>
std::pair<const typename attrib_traits<T>::native_type*, const_component_ptr_t> extract(const_entity_ptr_t e, const typename attrib_con_t::first_type& name) {
}
template<typename T>
bool operator!(const std::pair<T*, component_ptr_t>& lhs) {
	return !lhs.first || !lhs.second;
}
template<typename T>
bool operator!(const std::pair<const T*, const_component_ptr_t>& lhs) {
	return !lhs.first || !lhs.second;
}
}

#endif

